# -*- coding: utf-8 -*-
"""
Created on Fri Oct 12 21:46:18 2018

@author: HP
"""
import pandas as pd
import numpy as np
import time
import re
from sklearn.model_selection import KFold, cross_val_score, train_test_split
import datetime
from my_ort import ORT
from sklearn.preprocessing import RobustScaler
from sklearn.pipeline import make_pipeline

# 正交表生成类
ort = ORT()

# 评价函数
def rmse(y_true, y_pred):
    diff = y_pred - y_true
    sum_sq = sum(diff**2)    
    n = len(y_pred)   
    return np.sqrt(sum_sq/n)
# 供cv使用时：make_scorer(rmse, greater_is_better=False)

def save_res(res_arr, filename=''):
    test_ = pd.read_csv('../input/test.csv',index_col=0)
    res_df = pd.DataFrame({'Id':test_.index,'SalePrice':np.exp(res_arr)})
    t = datetime.datetime.now().strftime("%Y%m%d_%H_%M")
    res_df.to_csv('./res/'+filename+'%s.csv'%t, index=False)

class AdjustParam(object):
    '''
    model：最优模型
    ort_res: 正交试验结果
    param_res：各因素水平结果
    '''
    def __init__(self, X, y, scoring=None, cv=5):
        self.X = X
        self.y = y
        self.scoring = scoring
        self.cv = cv
        self.model = None
        self.ort_res = None
        self.param_res = None
    
    def run_ort(self, model, df_params):
        model_str = str(model)
        k=0
        n=len(df_params.index)
        df_params['ort_res'] = 0
        df_params['ort_res_std'] = 0
        df_params['ort_train_time'] = 0
        for i in df_params.index:
            k+=1
            params_li = list(map(lambda x:x+'='+ str(df_params.loc[i, x]),df_params.columns[:-3]))
            param_str = ', '.join(params_li)
            print(param_str)
            for re_p in params_li:
                p_val = re_p.split('=')[0]
                model_str = re.sub('%s=[^,)]*'%p_val, re_p ,model_str)
            model_ = eval(model_str)
            t1=time.time()
            cv_score = cross_val_score(model_, self.X, self.y, cv = 5, n_jobs = -1, scoring='neg_mean_squared_error' )
            err = np.sqrt(-cv_score)
            res = np.mean(err)
            res_std = np.std(err)
            t2 = int(time.time()-t1)
            print('res: %f, time: %d, num: %d/%d'%(res,t2,k,n))
            df_params.loc[i,'ort_res'] = res
            df_params.loc[i,'ort_res_std'] = res_std
            df_params.loc[i,'ort_train_time'] = t2
        self.ort_res = df_params
        # 筛选最优值
        res_li = list(map(lambda x: df_params.groupby(x).ort_res.mean().argmin(), df_params.columns[:-3]))
        param_li = df_params.columns[:-3]
        param_opt = list(map(lambda x: '='.join(x), zip(param_li,map(str,res_li))))
        # 最优RF模型
        for re_p in param_opt:
            p_val = re_p.split('=')[0]
            model_str = re.sub('%s=[^,)]*'%p_val, re_p ,model_str)
        self.model = eval(model_str)

        temp_li = []
        for i in df_params.columns[:-3]:
            temp = df_params.groupby(i).mean()[['ort_res','ort_res_std','ort_train_time']].sort_values(['ort_res','ort_res_std','ort_train_time'])
            temp = temp.rename(index = lambda x: str(i) + '=' + str(x))
            temp_li.append(temp)
        self.param_res = pd.concat(temp_li)

#%%


# 设定打印dataframe显示的列数量
pd.set_option('display.max_columns',80)
pd.set_option('display.max_rows',100)
#pd.set_option('display.height',100)
pd.set_option('display.width',65)
# 读文件
train_file = pd.read_csv('./input_processed/train.csv',index_col=0)
test_file = pd.read_csv('./input_processed/test.csv',index_col=0)

training, validation = train_test_split(train_file,test_size=0.2, random_state=17)

#%%
# 输入格式统一化
training_X = training.iloc[:,:-1]
training_y = training.iloc[:,-1]
training_y_log = np.log(training_y)
validation_X = validation.iloc[:,:-1]
validation_y = validation.iloc[:,-1]
validation_y_log = np.log(validation_y)
test_X = test_file

#%%
# 评价模型
def rmse_cv(model, n_folds=5):
    kf = KFold(n_folds, shuffle=True, random_state=42)
    rmse= np.sqrt(-cross_val_score(model, train_file.iloc[:,:-1].values, np.log(train_file.iloc[:,-1]), scoring="neg_mean_squared_error", cv = kf))
    rmse_mean = rmse.mean()
    rmse_std = rmse.std()
    return rmse_mean,rmse_std


#%%
from sklearn.linear_model import ElasticNet, Lasso,  BayesianRidge, LassoLarsIC
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.kernel_ridge import KernelRidge
from xgboost.sklearn import XGBRegressor
from lightgbm.sklearn import LGBMRegressor

lasso = make_pipeline(RobustScaler(), Lasso(alpha =0.0005, random_state=1))
ENet = make_pipeline(RobustScaler(), ElasticNet(alpha=0.0005, l1_ratio=.9, random_state=3))
KRR = KernelRidge(alpha=0.6, kernel='polynomial', degree=2, coef0=2.5)
GBoost = GradientBoostingRegressor(n_estimators=3000, learning_rate=0.05,
                                   max_depth=4, max_features='sqrt',
                                   min_samples_leaf=15, min_samples_split=10, 
                                   loss='huber', random_state =5)
model_xgb = XGBRegressor(colsample_bytree=0.4603, gamma=0.0468, 
                         learning_rate=0.05, max_depth=3, 
                         min_child_weight=1.7817, n_estimators=2200,
                         reg_alpha=0.4640, reg_lambda=0.8571,
                         subsample=0.5213, silent=1,
                         random_state =7, nthread = -1)
model_lgb = LGBMRegressor(objective='regression',num_leaves=5,
                          learning_rate=0.05, n_estimators=720,
                          max_bin = 55, bagging_fraction = 0.8,
                          bagging_freq = 5, feature_fraction = 0.2319,
                          feature_fraction_seed=9, bagging_seed=9,
                          min_data_in_leaf =6, min_sum_hessian_in_leaf = 11)


las_s = rmse_cv(lasso)
ENet_s = rmse_cv(ENet)
KRR_s = rmse_cv(KRR)
GBoost_s = rmse_cv(GBoost)
xgb_s = rmse_cv(model_xgb)
# (0.1279962189040785, 0.018185075377552616)
lgb_s = rmse_cv(model_lgb)


#%% 模型 RandomForestRegressor

#n_jobs 并行数 -1：与核数一致
rf_model = RandomForestRegressor(n_jobs=-1, oob_score=True, random_state=134)
rf_params = {'max_depth': [20,30,40], 'n_estimators': [500,600,700], 'max_features': [60,70,80]}  
ort.seeSets(3,see_num=100)
rf_params_df=ort.genSets(rf_params,mode=2)

rf = AdjustParam(training_X, training_y_log, 'neg_mean_squared_error')
rf.run_ort(model=rf_model, df_params=rf_params_df)
rf_model_opt = rf.model
rf_ort_res = rf.ort_res
rf_param_res = rf.param_res

rf_param_res
rf_model_opt = RandomForestRegressor(max_depth=30, n_estimators=600, max_features=70)
rf_model_opt.fit(training_X,training_y_log)
validation_y_log_predict = rf_model_opt.predict(validation_X)
rmse(validation_y_log, validation_y_log_predict)
#0.12635671064993356

#%% 模型 XGBregressor
################## 调整范围 生成合适的参数dataframe
#xgb_params = {'learning_rate': [0.06,0.08,0.1,0.12], 'n_estimators': [650,700,750,800], 'max_depth': [3,4,5,6], 'colsample_bytree':[0.55,0.6,0.65,0.7]}  
xgb_params = {'learning_rate': [0.08], 'n_estimators': [750], 'max_depth': [3], 'colsample_bytree':[0.1,0.15,0.2]}  
xgb_params = {'max_depth':[4,5,6],
'min_child_weight':[2,3,4],
'gamma':[0.3, 0.4, 0.5],
'subsample':[0.75, 0.8, 0.85],
'colsample_bytree':[0.9,0.95,1],
'n_estimators':[800,1000,1200],
'min_weight':[6,7,8],
'reg_alpha':[0.1,0.3,0.5],
'reg_lambda':[40,45,50],
'learning_rate':[0.1,0.3,0.5]}
ort.seeSets(10,see_num=100)
xgb_params_df = ort.genSets(xgb_params,mode=2)
######################################################
# 训练
xgb_model = XGBRegressor(n_jobs=-1)
xgb = AdjustParam(training_X, training_y_log, 'neg_mean_squared_error')
xgb.run_ort(model=xgb_model, df_params=xgb_params_df)
#xgb_model_opt = xgb.model
xgb_ort_res = xgb.ort_res
xgb_param_res = xgb.param_res
################## 选参：选取极差和方差都小的
xgb_param_res
#xgb_model_opt = XGBRegressor(learning_rate=0.06, max_depth=3,n_estimators=650, colsample_bytree=0.6)
xgb_model_opt = XGBRegressor(learning_rate=0.08, max_depth=3,n_estimators=750, colsample_bytree=0.2)

#xgb_model_opt = XGBRegressor(max_depth=5,min_child_weight=3,
#gamma=0.4,subsample=0.8,
#colsample_bytree=1.0,n_estimators=1000,
#min_weight=7,reg_alpha=0.1,
#reg_lambda=50,learning_rate=0.1)

#xgb_model_opt = XGBRegressor(max_depth=4,min_child_weight=2,
#gamma=0.3,subsample=0.85,
#colsample_bytree=1.0,n_estimators=800,
#min_weight=8,reg_alpha=0.3,
#reg_lambda=40,learning_rate=0.1)
rmse_cv(xgb_model_opt)
######################################################
# 验证最优模型
xgb_model_opt.fit(training_X,training_y_log)
validation_y_log_predict = xgb_model_opt.predict(validation_X)
rmse(validation_y_log, validation_y_log_predict)

# 结果预测
xgb_model_opt.fit(pd.concat([training_X,validation_X]), pd.concat([training_y_log,validation_y_log]))
test_predict = xgb_model_opt.predict(test_X)
save_res(test_predict)


#%%
# 直接dummy 均值填充
# validation-rf  : 0.13579013062998008
# validation-xgb : 0.12186200689055419
# 特征
#0.12251914001052963
#Na多的删 少的model预测 0.12001810400276995
#直接删去极偏  0.1203
#%%
training_X.skew()


#%%
from sklearn.metrics import mean_squared_error
def rmsle(y, y_pred):
    return np.sqrt(mean_squared_error(y, y_pred))

training_X = training.iloc[:,:-1]
training_y = training.iloc[:,-1]
training_y_log = np.log(training_y)
validation_X = validation.iloc[:,:-1]
validation_y = validation.iloc[:,-1]
validation_y_log = np.log(validation_y)
test_X = test_file

from tpot import TPOTRegressor
from sklearn.model_selection import train_test_split
'''
参数
generation：遗传算法进化次数，可理解为迭代次数
population_size：每次进化中种群大小
num_cv_folds：交叉验证
scoring：也就是损失函数
'''

tpot = TPOTRegressor(config_dict='TPOT light',generations=200, population_size=15, verbosity=2,warm_start=True)

tpot.fit(training_X, training_y_log)
print(tpot.score(validation_X, validation_y_log))
tpot.export('06tpot_price_predicet_pipeline.py')


tpot_predict = tpot.predict(validation_X)
rmsle(validation_y_log, tpot.predict(validation_X))

